package common;

import java.io.ObjectInputStream.GetField;

public final class Math3D {

	public static Point3D getXProduct(Point3D v1, Point3D v2){
		double x, y , z;
		x = v1.y * v2.z - v1.z * v2.y;
		y = v1.z * v2.x - v1.x * v2.z;
		z = v1.x * v2.y - v1.y * v2.x;
		return new Point3D(x, y, z);
	}


	public static double getDotProduct(Point3D v1, Point3D v2){
		return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z ;  
	}

	public static Point3D getNormal(Point3D p0, Point3D p1, Point3D p2){
		return getXProduct(getPoint(p1, p0), getPoint(p2, p0));
	}

	public static Point3D getPoint(Point3D p1, Point3D p0){
		return new Point3D(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z);
	}

	public static Point3D getAveragePoint(Point3D v1, Point3D v2){
		return new Point3D((v1.x + v2.x)/2, (v1.y + v2.y)/2, (v1.z + v2.z)/2);
	}

	public static Point3D addPoint(Point3D p1, Point3D p2){
		return new Point3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
	}
	public static Point3D subPoint(Point3D p1, Point3D p2){
		return new Point3D(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
	}

	public static Point3D scalePoint(Point3D point, double scalar){
		return new Point3D(point.x * scalar, point.y * scalar, point.z * scalar);
	}


	public static double getDistance(Point3D p1, Point3D p2) {
		return Math3D.subPoint(p1, p2).norm();
	}
	
	public static Point3D normalaize(Point3D p1){
		Point3D p = p1.clone();
		p.normalize();
		return p;
	}
	public static void fixToOrthogonal(Point3D v, Point3D o){
		double vnorm = v.norm();
		Point3D oxv = Math3D.getXProduct(v, o);
		
		if (oxv.norm() < 0.00000001){ 
			// guess direction
			v.setValues(v.x + 1, v.y, v.z);
			oxv = Math3D.getXProduct(v, o);

			if (oxv.norm() < 0.00000001){
				v.setValues(v.x, v.y+1, v.z);
				oxv = Math3D.getXProduct(v, o);
			}

		}
		
		Point3D vfx = Math3D.getXProduct(o, oxv);
		v.copy(vfx);
		v.normalize(vnorm);
	}
	
	
	public static boolean isRayHitShphere(Point3D source, Point3D target, Point3D sphere, double radius){
		Point3D t_target = Math3D.subPoint(target, source);
		Point3D t_sphere = Math3D.subPoint(sphere, source);
		Point3D t_ray = Math3D.normalaize(t_target);
		double dist = getDotProduct(t_sphere, t_ray);
		
		if (dist < 0) return false;
		if (dist > t_target.norm()) return false;
		
		t_ray.scale(dist);
		t_ray.sub(t_sphere);
		
		if (t_ray.norm() < radius) return true;
		
		return false;
	}
	
	// something like distance a ray travel inside sphere divided by sphere's diameter
	public static double raySphereIntersectionDistance(Point3D source, Point3D target, Point3D sphere, double radius){
		Point3D t_target = Math3D.subPoint(target, source);
		Point3D t_sphere = Math3D.subPoint(sphere, source);
		Point3D t_ray = Math3D.normalaize(t_target);
		double dist = getDotProduct(t_sphere, t_ray);
		
		if (dist < 0) return 0;
		if (dist > t_target.norm()) return 0;
		
		t_ray.scale(dist);
		t_ray.sub(t_sphere);
		
		if (t_ray.norm() > radius) return 0;
		return 1.0 - t_ray.norm()/ radius; 
	}
}
